#pragma once

#include "../mqcommon/mq_logger.hpp"
#include "../mqcommon/mq_helper.hpp"
#include "../mqcommon/mq_msg.pb.h"

#include <iostream>
#include <string>
#include <vector>
#include <mutex>
#include <unordered_map>

namespace mq
{
    // 交换机类
    struct Exchange
    {
        using ptr = std::shared_ptr<Exchange>;
        std::string name;                                  // 交换机名字
        ExchangeType type;                                 // 交换机类型
        bool durable;                                      // 交换机持久化标志位
        bool auto_delete;                                  // 交换机自动删除标志位 (还未实现该功能)
        google::protobuf::Map<std::string, std::string> args; // 其他参数 (方便以后扩展)

        Exchange(){};
        Exchange(const std::string &ename, ExchangeType etype, bool edurable, bool eauto_delete,
                 const google::protobuf::Map<std::string, std::string> &eargs)
            : name(ename), type(etype), durable(edurable), auto_delete(eauto_delete), args(eargs) {}

        // args存储键值对，在存储数据库的时候，会组织一个格式字符串进行存储 key=val&key=val....
        // 内部解析str_args字符串，将内容存储到成员中
        void setArgs(const std::string &str_args)
        {
            std::vector<std::string> result;
            StrHelper::split(str_args, "&", &result);
            for (auto &str : result)
            {
                auto pos = str.find('=');
                if (pos == std::string::npos)
                {
                    continue;
                }

                std::string key = str.substr(0, pos);
                std::string val = str.substr(pos + 1);
                args[key] = val;
            }
        }

        // 将args中的成员组织成字符串返回
        std::string getArgs()
        {
            std::string str;
            for (auto &it : args)
            {
                str += it.first;
                str += '=';
                str += it.second;
                str += '&';
            }

            // 字符串最后会多一个&,但是没影响,我们的split函数可以解决这个问题
            // key=val&key=val&
            return str;
        }
    };

    // 交换机数据持久化管理类--数据存储在sqlite数据库中
    class ExchangeMapper
    {
    private:
        SqliteHelper _sql_helper;
    private:
    //static修饰是为了消除形参的this指针
        static int selectCallBack(void* arg,int numcol,char** row,char** fields)
        {
            std::unordered_map<std::string, Exchange::ptr> * result = static_cast<std::unordered_map<std::string, Exchange::ptr>*>(arg);
            auto ecp = std::make_shared<Exchange>();
            ecp->name = row[0];
            ecp->type = (ExchangeType)std::stoi(row[1]);
            ecp->durable = (bool)std::stoi(row[2]);
            ecp->auto_delete = (bool)std::stoi(row[3]);
            if(row[4]) ecp->setArgs(row[4]);

            result->insert({ecp->name,ecp});
            return 0;
        }

    public:
        ExchangeMapper(const std::string &dbfile)
            : _sql_helper(dbfile)
        {
            // 创建父级目录
            const std::string path = FileHelper::parentDirectory(dbfile);
            FileHelper::createDirectory(path);

            // 创建/打开数据库文件
            assert(_sql_helper.open());

            // 创建交换机数据表
            createTable();
        }

        ~ExchangeMapper(){
            _sql_helper.close();
        }

        void createTable()
        {
            #define CREATE_TABLE "create table if not exists exchange_table(\
                                    name varchar(32) primary key, \
                                    type int, \
                                    durable int, \
                                    auto_delete int, \
                                    args varchar(128));"
            bool ret = _sql_helper.exec(CREATE_TABLE, nullptr, nullptr);
            if (ret == false)
            {
                DLOG("创建交换机数据库表失败!!");
                abort(); // 直接异常退出程序
            }
        }

        void removeTable()
        {
            #define DROP_TABLE "drop table if exists exchange_table;"
            bool ret = _sql_helper.exec(DROP_TABLE, nullptr, nullptr);
            if (ret == false)
            {
                DLOG("删除交换机数据库表失败!!");
                abort(); // 直接异常退出程序
            }
        }

        bool insert(Exchange::ptr &ecp)
        {
            std::stringstream ss;
            ss << "insert into exchange_table values(";
            ss << "'" << ecp->name << "',";
            ss << ecp->type << ",";
            ss << ecp->durable << ",";
            ss << ecp->auto_delete << ",";
            ss << "'" << ecp->getArgs() << "');";
            return _sql_helper.exec(ss.str(), nullptr, nullptr);
        }

        bool remove(const std::string &name)
        {
            std::stringstream ss;
            ss << "delete from exchange_table where name = ";
            ss << "'" << name << "';";
            return _sql_helper.exec(ss.str(), nullptr, nullptr);
        }

        //返回交换机表中所有数据,用于重启后恢复
        std::unordered_map<std::string, Exchange::ptr> recovery(){
            std::unordered_map<std::string, Exchange::ptr> res;
            std::string sql = "select name, type, durable, auto_delete, args from exchange_table";
            _sql_helper.exec(sql, selectCallBack, &res);

            return res;
        }
    };

    //交换机数据内存管理类，这个类才是对外提供的
    class ExchangeManager
    {
    private:
        std::mutex _mutex;  //这个类对象可能被多线程访问，我们要加锁
        ExchangeMapper _mapper;
        std::unordered_map<std::string,Exchange::ptr> _Exchanges;   //管理已经存在的交换机
    public:
        using ptr = std::shared_ptr<ExchangeManager>;

        ExchangeManager(const std::string &dbfile)
            :_mapper(dbfile)
        {
            //恢复交换机
            _Exchanges = _mapper.recovery();
        }

        //声明交换机，存在则ok，不存在则创建
        bool declareExchange(const std::string &name,
            ExchangeType type, bool durable, bool auto_delete,
            const google::protobuf::Map<std::string, std::string> &args)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _Exchanges.find(name);
            if(it != _Exchanges.end()){
                //存在直接return
                return true;
            }

            //定义一个Exchange对象
            auto ecp = std::make_shared<Exchange>(name,type,durable,auto_delete,args);
             //插入进数据库
            if(durable == true) {
                bool ret = _mapper.insert(ecp);
                if(ret == false) return false;
            }
            _Exchanges.insert({name,ecp});
            return true;
        }
        //删除指定交换机
        void deleteExchange(const std::string &name){
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _Exchanges.find(name);
            if(it == _Exchanges.end()){
                return;
            }

            //删除数据库中数据
            if(it->second->durable == true){
                _mapper.remove(name);
            }
            _Exchanges.erase(name);
        }
        //获取指定交换机
        Exchange::ptr selectExchange(const std::string &name){
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _Exchanges.find(name);
            if(it == _Exchanges.end()){
                //交换机不存在
                return Exchange::ptr();
            }

            return it->second;
        }
        //指定交换机是否存在
        bool exists(const std::string &name){
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _Exchanges.find(name);
            if(it == _Exchanges.end()){
                return false;
            }

            return true;
        }
        //获取交换机个数
        size_t size(){            
            std::unique_lock<std::mutex> lock(_mutex);
            return _Exchanges.size();
        }

        //销毁所有交换机 这个接口用于gtest单元测试
        void clear(){
            std::unique_lock<std::mutex> lock(_mutex);
            _mapper.removeTable();
            _Exchanges.clear();
        }
    };
}
